import Duck from 'extensible-duck'
import produce from 'immer'
import { createSelector } from 'reselect'
import { ITEM_PAGE_LIMIT } from 'utils/constants'
import { item as normItem, project as normProject } from './norm'

const classesSelector = state => state.projectLabelView.info.entities.classes
const activeclassSelector = state => state.projectLabelView.activeClass
const activeclassObjectSelector = createSelector(
  classesSelector,
  activeclassSelector,
  (classes, activeClass) => classes[activeClass],
)

const tagsSelectors = (state) => {
  const editlabelId = state.projectLabelView.editInfo.dynamicValue.editLabelId
  if (editlabelId === undefined) {
    console.log('editlabelId is null')
    return undefined
  }
  const editLabel = state.projectLabelView.itemsPage.activeItem.entities.labels[editlabelId]
  const labelClass = state.projectLabelView.info.entities.classes[editLabel.class_id]
  if (!labelClass || labelClass.setting.tags.length === 0) {
    console.log('tags is null')
    return undefined
  }
  return {
    tags: labelClass.setting.tags,
    values: editLabel.tags,
  }
}

const tagChangeReducer = (action, state) => {
  const editlabelId = state.editInfo.dynamicValue.editLabelId
  const editLabel = state.itemsPage.activeItem.entities.labels[editlabelId]
  editLabel.tags = editLabel.tags.reduce(
    (pre, next) => {
      if (next.name && next.name !== action.tag) {
        const { name, value } = next
        pre.push({ name, value })
      }
      return pre
    },
    [{ name: action.tag, value: action.value }],
  )
}

const tagSelector = (state, id) => state.projectLabelView.info.entities.tags[id]

const activeclassTypeSelector = createSelector(
  classesSelector,
  activeclassSelector,
  (classes, activeClass) => classes[activeClass].type,
)

const infoSelector = (state) => {
  const id = state.projectLabelView.info.result
  return state.projectLabelView.info.entities.project[id]
}
const activeBrushesSelector = createSelector(
  classesSelector,
  infoSelector,
  (classes, info) => info.classes.reduce((pre, next) => {
    pre.add(classes[next].type)
    return pre
  }, new Set()),
)
const defaultListSelector = createSelector(
  infoSelector,
  (info) => {
    let result
    if (info.unfinished > 0) {
      result = 'unfinished'
    } else if (info.reopend > 0) {
      result = 'reopend'
    } else if (info.finished > 0) {
      result = 'finished'
    } else {
      result = undefined
    }
    return result
  },
)

const reducerGetLabels = (state) => {
  const itemId = state.itemsPage.activeItem.result
  return state.itemsPage.activeItem.entities.items[itemId].label
}

const calcNewPoint = (action, state) => {
  const id = state.editInfo.dynamicValue.editLabelId
  const editLabel = state.itemsPage.activeItem.entities.labels[id]
  editLabel.exterior[action.i][0] += action.x
  editLabel.exterior[action.i][1] += action.y
}

const changeKeyPointType = (action, state) => {
  const id = state.editInfo.dynamicValue.editLabelId
  const editLabel = state.itemsPage.activeItem.entities.labels[id]
  const typePoint = editLabel.exterior[action.i][2]
  if (typePoint === undefined) {
    editLabel.exterior[action.i][2] = 1
  } else {
    editLabel.exterior[action.i][2] = (typePoint + 1) % 3
  }
}

const changeLbaelClass = (action, state) => {
  const id = state.editInfo.dynamicValue.editLabelId
  const editLabel = state.itemsPage.activeItem.entities.labels[id]
  editLabel.class_id = action.value
}

const changelabelState = (action, state) => {
  if (state.itemsPage.items !== undefined) {
    state.itemsPage.items.map((item) => {
      if (item.id === action.value.id) {
        item.state = action.value.state
      }
    })
  }
}

const reducerGetActiveItemId = state => state.itemsPage.activeItem.result

const InitDynamicValue = {
  pointer: [0, 0],
  showLabelId: undefined,
  editLabelId: undefined,
  prepareLabel: {
    points: [],
  },
  polygon: {
    type: 'edge',
    mode: 'append',
    showHoleIndex: undefined,
    editHoleIndex: undefined,
  },
  editTag: {},
}

const editInfoInitState = {
  staticValues: {
    rectangle: {
      useGuideLines: true,
    },
  },
  dynamicValue: InitDynamicValue,
}

export default new Duck({
  namespace: 'label',
  store: 'projectLabelView',
  types: [
    'LOAD_INFO',
    'RECIVE_INFO',
    'REMOVE_INFO',
    'SELECT_BRUSH',
    'INIT_ITEM_PAGE',
    'RECIVE_ITEM_PAGE',
    'GET_ITEM',
    'RECIVE_ITEM',
    'HIDE_LABEL',
    'HIDE_ALL',
    'DELETE_ALL_SERVER',
    'DELETE_LABEL_SERVER',
    'DELETE_ALL',
    'DELETE_LABEL',
    'OPEN_DELETE_MODAL',
    'CLOSE_DELETE_MODAL',
    'ACTIVE_LABEL',
    'SET_EDIT_LABEL_ID',
    'PAINT_POINT',
    'RECIVE_POINT',
    'MOVE_POINT',
    'UPDATE_LABEL',
    'RESET_EDIT_INFO',
    'LABEL_ADD_POINT',
    'NEW_LABEL',
    'PREPARE_LABEL',
    'RECIVE_NEW_LABEL',
    'DO_AND_UPDATE',
    'ADD_INTERIOR_POINT',
    'ADD_EXTERIOR_POINT',
    'FINISH_LABEL',
    'RECIVE_PROJECT_INFO',
    'RECIVE_TASK_ITEM_PAGE',
    'RECIVE_PAGE_INFO',
    'CHANGE_PAGE_INFO_STATE',
    'SET_ACTIVE_ITEM_ID',
    'CHANGE_LABEL_CLASS',
    'SET_PAGE_INFO_OFFSET',
    'NEXT_PAGE',
    'PRE_PAGE',
    'ITEM_NAV',
    'FIRST_PAGE',
    'LAST_PAGE',
    'FINISH_ITEM',
    'REOPEN_ITEM',
    'CHANGE_ITEM_STATE',
    'POINTER_MOVE',
    'RIGHT_CLICK_POINT',
    'EDIT_TAG_START',
    'EDIT_TAG_CANCEL',
    'EDIT_TAG_CHANGE',
    'EDIT_TAG_CONFIRM',
  ],
  initialState: {
    pageInfo: {
      total: undefined,
      limit: ITEM_PAGE_LIMIT,
      offset: 0,
      state: ['label'],
    },
    itemsPage: {},
    isDeleteAllModalOpen: false,
    editInfo: editInfoInitState,
  },
  reducer: produce((draft, action, duck) => {
    switch (action.type) {
      case duck.types.REMOVE_INFO:
        draft.info = undefined
        break
      case duck.types.RECIVE_INFO:
        draft.info = normProject(action.projectInfo)
        break
      case duck.types.SELECT_BRUSH:
        draft.selectedBrush = action.brush
        break
      case duck.types.RECIVE_ITEM_PAGE:
        draft.itemsPage.items = action.items
        break
      case duck.types.RECIVE_ITEM:
        draft.itemsPage.activeItem = normItem(action.itemInfo)
        break
      case duck.types.HIDE_ALL:
        reducerGetLabels(draft).forEach((label) => {
          draft.itemsPage.activeItem.entities.labels[label].hide = action.hide
        })
        break
      case duck.types.HIDE_LABEL:
        draft.itemsPage.activeItem.entities.labels[action.id].hide = action.hide
        break
      case duck.types.DELETE_ALL:
        draft.itemsPage.activeItem.entities.items[draft.itemsPage.activeItem.result].label = []
        draft.itemsPage.activeItem.entities.labels = {}
        break
      case duck.types.DELETE_LABEL:
        draft.itemsPage.activeItem.entities.items[draft.itemsPage.activeItem.result].label.splice(
          action.index,
          1,
        )
        delete draft.itemsPage.activeItem.entities.labels[action.id]
        break
      case duck.types.OPEN_DELETE_MODAL:
        draft.isDeleteAllModalOpen = true
        break
      case duck.types.CLOSE_DELETE_MODAL:
        draft.isDeleteAllModalOpen = false
        break
      case duck.types.ACTIVE_LABEL:
        draft.activeClass = action.value
        break
      case duck.types.SET_EDIT_LABEL_ID:
        draft.editInfo.dynamicValue.editLabelId = action.id
        break
      case duck.types.UPDATE_LABEL:
        draft.itemsPage.activeItem.entities.labels[action.label.id] = action.label
        break
      case duck.types.RESET_EDIT_INFO:
        draft.editInfo.dynamicValue = InitDynamicValue
        break
      case duck.types.PREPARE_LABEL:
        draft.editInfo.dynamicValue.prepareLabel.points.push([action.x, action.y])
        break
      case duck.types.RECIVE_NEW_LABEL:
        draft.itemsPage.activeItem.entities.labels[action.label.id] = action.label
        draft.itemsPage.activeItem.entities.items[draft.itemsPage.activeItem.result].label.push(
          action.label.id,
        )
        break
      case duck.types.ADD_INTERIOR_POINT:
        draft.itemsPage.activeItem.entities.labels[action.id].interior[action.index].push([
          action.x,
          action.y,
        ])
        break
      case duck.types.ADD_EXTERIOR_POINT:
        draft.itemsPage.activeItem.entities.labels[action.id].exterior.push([action.x, action.y])
        break
      case duck.types.RECIVE_PROJECT_INFO:
        draft.info = normProject(action.projectInfo)
        break
      case duck.types.RECIVE_TASK_ITEM_PAGE:
        draft.itemsPage.items = action.itemPage
        if (action.itemPage.length > 0) {
          draft.itemsPage.activeItemId = action.itemPage[0].id
        } else {
          draft.itemsPage.activeItemId = undefined
        }
        break
      case duck.types.RECIVE_PAGE_INFO:
        draft.pageInfo.total = action.info
        draft.pageInfo.offset = 0
        break
      case duck.types.CHANGE_PAGE_INFO_STATE:
        if (action.state === 'label') {
          draft.pageInfo.state = ['label']
        } else if (action.state === 'done') {
          draft.pageInfo.state = ['done']
        } else if (action.state === 'all') {
          draft.pageInfo.state = ['label', 'done']
        } else if (action.state === 'reopen') {
          draft.pageInfo.state = ['reopen']
        }
        break
      case duck.types.SET_ACTIVE_ITEM_ID:
        draft.itemsPage.activeItemId = action.id
        break
      case duck.types.MOVE_POINT:
        calcNewPoint(action, draft)
        break
      case duck.types.RIGHT_CLICK_POINT:
        changeKeyPointType(action, draft)
        break
      case duck.types.CHANGE_LABEL_CLASS:
        changeLbaelClass(action, draft)
        break
      case duck.types.SET_PAGE_INFO_OFFSET:
        draft.pageInfo.offset = action.value
        break
      case duck.types.NEXT_PAGE:
        draft.pageInfo.offset += ITEM_PAGE_LIMIT
        if (draft.pageInfo.offset >= draft.pageInfo.total) {
          draft.pageInfo.offset = 0
        }
        break
      case duck.types.PRE_PAGE:
        draft.pageInfo.offset -= ITEM_PAGE_LIMIT
        if (draft.pageInfo.offset < 0) {
          draft.pageInfo.offset = draft.pageInfo.total
            - (draft.pageInfo.total % ITEM_PAGE_LIMIT === 0
              ? ITEM_PAGE_LIMIT
              : draft.pageInfo.total % ITEM_PAGE_LIMIT)
        }
        break
      case duck.types.FIRST_PAGE:
        draft.pageInfo.offset = 0
        break
      case duck.types.LAST_PAGE:
        draft.pageInfo.offset = draft.pageInfo.total
          - (draft.pageInfo.total % ITEM_PAGE_LIMIT === 0
            ? ITEM_PAGE_LIMIT
            : draft.pageInfo.total % ITEM_PAGE_LIMIT)
        break
      case duck.types.CHANGE_ITEM_STATE:
        changelabelState(action, draft)
        break
      case duck.types.POINTER_MOVE:
        draft.editInfo.dynamicValue.pointer = [action.x, action.y]
        break
      case duck.types.EDIT_TAG_START:
        draft.editInfo.dynamicValue.editTag.tag = action.tag
        draft.editInfo.dynamicValue.editTag.value = action.value
        draft.editInfo.dynamicValue.editTag.initValue = action.value
        break
      case duck.types.EDIT_TAG_CANCEL:
        draft.editInfo.dynamicValue.editTag = {}
        break
      case duck.types.EDIT_TAG_CHANGE:
        draft.editInfo.dynamicValue.editTag.value = action.value
        break
      case duck.types.EDIT_TAG_CONFIRM:
        tagChangeReducer(action, draft)
        draft.editInfo.dynamicValue.editTag = {}
        break
      default:
        break
    }
  }),
  selectors: {
    classes: classesSelector,
    activeclass: activeclassSelector,
    activeclassType: activeclassTypeSelector,
    activeclassObject: activeclassObjectSelector,
    activeBrushes: activeBrushesSelector,
    defaultImageListType: defaultListSelector,
    selectedBrush: state => state.projectLabelView.selectedBrush,
    info: infoSelector,
    isProjectHasClass: state => state.projectLabelView.info && state.projectLabelView.info.entities.classes !== undefined,
    isProjectInit: state => state.projectLabelView.info !== undefined,
    projectId: state => state.projectLabelView.info.result,
    itemsPage: state => state.projectLabelView.itemsPage,
    pageItemsInit: (state) => {
      if (state.projectLabelView.itemsPage !== undefined) {
        return state.projectLabelView.itemsPage.items !== undefined
      }
      return false
    },
    pageItems: state => state.projectLabelView.itemsPage.items,
    firstItem: state => state.projectLabelView.itemsPage.items[0],
    activeItem: state => state.projectLabelView.itemsPage.activeItem,
    isItemInit: state => state.projectLabelView.itemsPage.activeItem !== undefined,
    activeItemId: state => state.projectLabelView.itemsPage.activeItemId,
    labels: (state) => {
      const itemId = state.projectLabelView.itemsPage.activeItem.result
      return state.projectLabelView.itemsPage.activeItem.entities.items[itemId].label
    },
    labelsObject: state => state.projectLabelView.itemsPage.activeItem.entities.labels,
    label: (state, id) => state.projectLabelView.itemsPage.activeItem.entities.labels[id],
    isAllHide: (state) => {
      const { labels } = state.projectLabelView.itemsPage.activeItem.entities
      const itemId = state.projectLabelView.itemsPage.activeItem.result
      const labelIds = state.projectLabelView.itemsPage.activeItem.entities.items[itemId].label
      if (labelIds.length === 0) {
        return false
      }
      for (let i = 0; i < labelIds.length; i += 1) {
        if (labels[labelIds[i]].hide !== true) {
          return false
        }
      }
      return true
    },
    isDeleteAllModalOpen: state => state.projectLabelView.isDeleteAllModalOpen,
    labelNumer: (state) => {
      const itemId = state.projectLabelView.itemsPage.activeItem.result
      return state.projectLabelView.itemsPage.activeItem.entities.items[itemId].label.length
    },
    imageSrc: (state) => {
      const itemId = state.projectLabelView.itemsPage.activeItem.result
      return state.projectLabelView.itemsPage.activeItem.entities.items[itemId].image.path
    },
    imageInfo: (state) => {
      const itemId = state.projectLabelView.itemsPage.activeItem.result
      return state.projectLabelView.itemsPage.activeItem.entities.items[itemId].image
    },
    editLabelId: state => state.projectLabelView.editInfo.dynamicValue.editLabelId,
    isEditingLabel: state => state.projectLabelView.editInfo.dynamicValue.editLabelId !== undefined,
    editLabel: (state) => {
      const id = state.projectLabelView.editInfo.dynamicValue.editLabelId
      return state.projectLabelView.itemsPage.activeItem.entities.labels[id]
    },
    activeItemState: (state) => {
      const itemId = state.projectLabelView.itemsPage.activeItemId
      const { activeItem } = state.projectLabelView.itemsPage
      if (
        itemId !== undefined
        && activeItem !== undefined
        && activeItem.entities.items[itemId] !== undefined
      ) {
        return activeItem.entities.items[itemId].state
      }
      return undefined
    },
    activeItemCreateTime: (state) => {
      const itemId = state.projectLabelView.itemsPage.activeItemId
      const { activeItem } = state.projectLabelView.itemsPage
      if (
        itemId !== undefined
        && activeItem !== undefined
        && activeItem.entities.items[itemId] !== undefined
      ) {
        return activeItem.entities.items[itemId].createTime
      }
      return undefined
    },
    activeItemFinishTime: (state) => {
      const itemId = state.projectLabelView.itemsPage.activeItemId
      const { activeItem } = state.projectLabelView.itemsPage
      if (
        itemId !== undefined
        && activeItem !== undefined
        && activeItem.entities.items[itemId] !== undefined
      ) {
        return activeItem.entities.items[itemId].finishTime
      }
      return undefined
    },
    editHoleIndex: state => state.projectLabelView.editInfo.dynamicValue.polygon.editHoleIndex,
    isEditPolygonHole: state => state.projectLabelView.editInfo.dynamicValue.polygon.type === 'hole',
    prePareLabel: state => state.projectLabelView.editInfo.dynamicValue.prepareLabel,
    pageInfo: state => state.projectLabelView.pageInfo,
    pointer: state => state.projectLabelView.editInfo.dynamicValue.pointer,
    isGuideLineActive: state => state.projectLabelView.selectedBrush === 'rectangle'
      && state.projectLabelView.editInfo.dynamicValue.editLabelId === undefined,
    editItemTags: tagsSelectors,
    tag: tagSelector,
    selectEditTag: state => state.projectLabelView.editInfo.dynamicValue.editTag,
  },
  creators: duck => ({
    actionLoadProjectInfo: projectId => ({ type: duck.types.LOAD_INFO, projectId }),
    actionReciveProjectInfo: projectInfo => ({ type: duck.types.RECIVE_INFO, projectInfo }),
    actionRemoveProjectInfo: () => ({ type: duck.types.REMOVE_INFO }),
    actionSelectBrush: brush => ({ type: duck.types.SELECT_BRUSH, brush }),
    actionLoadItemPage: itemId => ({ type: duck.types.INIT_ITEM_PAGE, itemId }),
    actionReciveItemPage: items => ({ type: duck.types.RECIVE_ITEM_PAGE, items }),
    actionGetItemInfo: itemId => ({ type: duck.types.GET_ITEM, itemId }),
    actionReciveItemInfo: itemInfo => ({ type: duck.types.RECIVE_ITEM, itemInfo }),
    actionToggleHide: (id, hide) => ({ type: duck.types.HIDE_LABEL, id, hide }),
    actionToggleHideAll: hide => ({ type: duck.types.HIDE_ALL, hide }),
    actionDeleteAll: () => ({ type: duck.types.DELETE_ALL }),
    actionDelete: (id, index) => ({ type: duck.types.DELETE_LABEL, id, index }),
    actionDeleteAllServer: () => ({ type: duck.types.DELETE_ALL_SERVER }),
    actionDeleteServer: (id, index) => ({ type: duck.types.DELETE_LABEL_SERVER, id, index }),
    actionOpenDeleteModal: () => ({ type: duck.types.OPEN_DELETE_MODAL }),
    actionCloseDeleteModal: () => ({ type: duck.types.CLOSE_DELETE_MODAL }),
    actionSetActiveClass: value => ({
      type: duck.types.ACTIVE_LABEL,
      value,
    }),
    actionSetEditLabelId: id => ({ type: duck.types.SET_EDIT_LABEL_ID, id }),
    actionPaintPoint: (x, y) => ({ type: duck.types.PAINT_POINT, x, y }),
    actionRecivePoint: (x, y) => ({ type: duck.types.RECIVE_POINT, x, y }),
    actionRightClick: i => ({
      type: duck.types.RIGHT_CLICK_POINT,
      i,
    }),
    actionMovePoint: (i, x, y) => ({
      type: duck.types.MOVE_POINT,
      i,
      x,
      y,
    }),
    actionMoveHolePoint: () => {},
    actionUpdateLabel: (label, delay) => ({ type: duck.types.UPDATE_LABEL, label, delay }),
    actionResetEditAndDo: (action, ...args) => ({ type: duck.types.RESET_EDIT_INFO, action, args }),
    actionNewLabel: (x, y) => ({ type: duck.types.NEW_LABEL, x, y }),
    actionPrepareLabel: (x, y) => ({ type: duck.types.PREPARE_LABEL, x, y }),
    actionLabelAddPoint: (x, y) => ({ type: duck.types.LABEL_ADD_POINT, x, y }),
    actionReciveNewLable: label => ({ type: duck.types.RECIVE_NEW_LABEL, label }),
    actionDoAndUpdate: (id, action, ...args) => ({
      type: duck.types.DO_AND_UPDATE,
      id,
      action,
      args,
    }),
    actionAddInteriorPoint: (id, index, x, y) => ({
      type: duck.types.ADD_INTERIOR_POINT,
      id,
      index,
      x,
      y,
    }),
    actionAddExteriorPoint: (id, x, y) => ({
      type: duck.types.ADD_EXTERIOR_POINT,
      id,
      x,
      y,
    }),
    actionFinishLabel: () => ({
      type: duck.types.FINISH_LABEL,
    }),
    actionRecivePageInfo: info => ({
      type: duck.types.RECIVE_PAGE_INFO,
      info,
    }),
    actionChangePageInfoState: state => ({
      type: duck.types.CHANGE_PAGE_INFO_STATE,
      state,
    }),
    actionReciveProject: projectInfo => ({
      type: duck.types.RECIVE_PROJECT_INFO,
      projectInfo,
    }),
    actionReciveTaskItemPage: itemPage => ({
      type: duck.types.RECIVE_TASK_ITEM_PAGE,
      itemPage,
    }),
    actionSetActiveItemId: id => ({
      type: duck.types.SET_ACTIVE_ITEM_ID,
      id,
    }),
    actionChangeLabelClass: value => ({
      type: duck.types.CHANGE_LABEL_CLASS,
      value,
    }),
    actionSetPageInfoOffset: value => ({
      type: duck.types.SET_PAGE_INFO_OFFSET,
      value,
    }),
    actionNextPage: () => ({
      type: duck.types.NEXT_PAGE,
    }),
    actionPrePage: () => ({
      type: duck.types.PRE_PAGE,
    }),
    actionFirstPage: () => ({
      type: duck.types.FIRST_PAGE,
    }),
    actionLastPage: () => ({
      type: duck.types.LAST_PAGE,
    }),
    actionChangeItemState: value => ({
      type: duck.types.CHANGE_ITEM_STATE,
      value,
    }),
    actionPointerMove: (x, y) => ({
      type: duck.types.POINTER_MOVE,
      x,
      y,
    }),
    actionNavItem: (url, state, navType) => ({
      type: duck.types.ITEM_NAV,
      url,
      state,
      navType,
    }),
    actionEditTagStart: (tag, value) => ({
      type: duck.types.EDIT_TAG_START,
      tag,
      value,
    }),
    actionEditTagCancel: () => ({
      type: duck.types.EDIT_TAG_CANCEL,
    }),
    actionEditTagChange: value => ({
      type: duck.types.EDIT_TAG_CHANGE,
      value,
    }),
    actionTagChange: (tag, value) => ({
      type: duck.types.EDIT_TAG_CONFIRM,
      tag,
      value,
    }),
  }),
})
